﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using UMC.Net;

namespace UMC.ITME
{

    class HttpMimeSocket : HttpMime
    {

        internal StringValue _remoteIpAddress, _finger = StringValue.Empty;
        public override StringValue Finger => _finger;
        public HttpMimeSocket(Socket socket, StringValue iPAddress)
        {
            this._socket = socket;
            this.ActiveTime = UMC.Data.Utility.TimeSpan();
            this.pid = socket.GetHashCode();
            this._remoteIpAddress = iPAddress;

            this.IsWritedIP = iPAddress.Span[0] != 127;
            HttpMimeServier.httpMimes.TryAdd(pid, this);
            Read(Memory<byte>.Empty);
        }

        public HttpMimeSocket(Socket socket, StringValue iPAddress, Memory<byte> buffer, bool is_writed_ip)
        {
            this.IsWritedIP = is_writed_ip; //iPAddress.Span[0] != 127;
            this._socket = socket;
            this.ActiveTime = UMC.Data.Utility.TimeSpan();
            this.pid = socket.GetHashCode();
            this._remoteIpAddress = iPAddress;
            HttpMimeServier.httpMimes.TryAdd(pid, this);
            Read(buffer);
        }
        private bool IsWritedIP = true;
        public override StringValue RemoteIpAddress => _remoteIpAddress;
        Socket _socket;
        int pid = 0;
        public override int Id => pid;

        StringValue _Host = new StringValue("127.0.0.1");
        public override StringValue Host => _Host;

        protected override void Send(byte[] buffer, int offset, int count)
        {
            if (isDispose == false)
            {
                try
                {
                    _socket.Send(buffer, offset, count, SocketFlags.None);
                }
                catch
                {
                    this.Dispose();
                }
            }
        }
        async void Read(Memory<byte> memory)
        {

            byte[] data = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
            memory.CopyTo(data);
            try
            {
                this.ActiveTime = UMC.Data.Utility.TimeSpan();
                uint reqTime = 0;
                uint lastTime = this.ActiveTime;
                int start = 0, size = memory.Length; //data.Length;
                if (size > 0)
                {
                    var netLoger = new HttpLoger(new ByteChunk());

                    if (IsWritedIP)
                    {
                        netLoger.Write(HttpLogerType.IP, this._remoteIpAddress);
                    }
                    this.Request = new HttpMimeRequest(this, this.MimeMemory, netLoger);
                    var req = this.Request; 
                    var len = req.Receive(data, 0, size);
                    if (req.IsHttpFormatError)
                    {
                        this.OutText(400);
                        return;
                    }

                    if (len > 0)
                    {
                        Array.Copy(data, size - len, data, 0, len);
                        start = len;
                        size = data.Length - len;
                    }
                    else
                    {
                        start = 0;
                        size = data.Length;
                    }

                }

                while ((size = await _socket.ReceiveAsync(new ArraySegment<byte>(data, start, size))) > 0)
                {

                    this.ActiveTime = UMC.Data.Utility.TimeSpan();

                    var readTime = this.ActiveTime - lastTime;
                    if (this.Request == null || this.Request?.IsMimeFinish == true)
                    {
                        readTime = 0;
                        reqTime = this.ActiveTime;
                        var netLoger = new HttpLoger(new ByteChunk());

                        if (IsWritedIP)
                        {
                            netLoger.Write(HttpLogerType.IP, this._remoteIpAddress);
                        }
                        this.MimeMemory.Reset();
                        this.Request = new HttpMimeRequest(this, this.MimeMemory, netLoger);
                    }
                    var req = this.Request;
                    size += start;
                    var len = req.Receive(data, 0, size);
                    if (req.IsHttpFormatError)
                    {
                        this.OutText(400);
                        break;
                    }

                    if (len > 0)
                    {
                        Array.Copy(data, size - len, data, 0, len);
                        start = len;
                        size = data.Length - len;
                    }
                    else
                    {
                        start = 0;
                        size = data.Length;
                    }


                    lastTime = UMC.Data.Utility.TimeSpan();


                }


            }
            catch (System.OutOfMemoryException)
            {
                GC.Collect();
                this.Dispose();
            }
            catch// (Exception ex)
            {
                this.Dispose();

            }

        }
        TlsSite tlsSite = null;
        public override object Tag => tlsSite;// base.Tag;
        public override void PrepareRespone(HttpMimeRequest request)
        {
            if (request._Challenge.IsEmpty() == false)
            {
                var t = System.Buffers.ArrayPool<byte>.Shared.Rent(256);
                try
                {
                    int l = Utility.DecodeBase64(request._Challenge.Span, t);
                    tlsSite = WebServlet.GetSiteInfo(request._host);
                    tlsSite.Fingerprint = new StringValue(t.AsSpan(0, l).ToArray());
                    tlsSite.Finger = new StringValue(Utility.MD5(tlsSite.Fingerprint.Span));
                    // tlsSite.FingerIndex = tlsSite.Fingerprint.Length / 2;
                    if (request._XProxy.Length > 0)
                    {
                        l = Utility.DecodeBase64(request._XProxy.Span, t);
                        tlsSite.ProxyCode = new StringValue(t.AsSpan(0, l).ToArray()); ;// request._XProxy;
                    }
                    request.netLoger.Write(HttpLogerType.Fingerprint, tlsSite.Finger);
                }
                finally
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(t);
                }
            }
            base.PrepareRespone(request);
        }
        bool isDispose = false;
        public override void Dispose()
        {
            if (isDispose == false)
            {
                this.MimeMemory.Dispose();
                isDispose = true;
                try
                {
                    if (this.Request is IDisposable)
                    {
                        ((IDisposable)this.Request).Dispose();
                    }
                }
                catch
                {

                }
                _socket?.Close();
                _socket?.Dispose();
            }
            HttpMimeServier.httpMimes.TryRemove(pid, out var _);

        }
    }
}

